<template>
    <div class="tnxel-query-table" :id="id" :class="{selectable: selectable}">
        <el-table ref="table" :data="records" scrollbar-always-on
            :size="size" :border="border" :stripe="stripe" :max-height="tableMaxHeight"
            @sort-change="sort" :default-sort="defaultSort" :key="tableKey"
            :default-expand-all="defaultExpandAll" :span-method="spanMethod"
            :row-class-name="rowClassName" @cell-click="selectRow" v-loading="records === null">
            <el-table-column header-align="center" align="center" :width="selectable === 'all' ? 40 : 54"
                v-if="selectable">
                <template #header>
                    <el-checkbox :model-value="pageAllSelected" :indeterminate="allSelectedIndeterminate"
                        @change="selectAll" v-if="selectable === 'all'"/>
                    <span v-else>选择</span>
                </template>
                <template #default="scope">
                    <el-checkbox v-model="pageSelectedIndexes[scope.$index]" @change="selectPageToAll"
                        v-if="selectable === 'multi' || selectable === 'all'"/>
                </template>
            </el-table-column>
            <slot></slot>
            <template #empty>
                <template v-if="records === null">
                    <tnxel-icon value="Loading" :size="18" v-if="querying"/>
                    <template v-else>
                        <span>尚未开始查询</span>
                        <span v-if="paramRequired && emptyParams">，请至少提供一个查询条件</span>
                    </template>
                </template>
                <span v-else>&lt;空&gt;</span>
            </template>
            <template #append v-if="records && records.length && paged && typeof paged.total !== 'number'">
                <tnxel-button class="py-0 px-1 mx-2 border-0" icon="Top" tooltip="回到顶部" plain
                    @click="scrollToTop"/>
                <tnxel-icon class="text-secondary" value="Loading" :size="18" v-if="querying"/>
                <el-button type="primary" link class="m-0" @click="onPagedChange(paged.pageNo + 1)"
                    v-else-if="paged.morePage">
                    加载更多
                </el-button>
                <el-button type="info" link v-else>已没有更多</el-button>
                <tnxel-button class="py-0 px-1 mx-2 border-0" icon="Top" tooltip="回到顶部" plain @click="scrollToTop"/>
            </template>
        </el-table>
        <slot name="paged" :paged="paged" :show="showPaged" :query="query"
            v-if="params.pageSize > 0 && paged && typeof paged.total === 'number'">
            <tnxel-paged :value="paged" :change="onPagedChange" :align="pagedAlign"
                :page-sizes="pagedSizes" :layout="pagedLayout" v-if="showPaged"/>
        </slot>
    </div>
</template>

<script>
import $ from 'cash-dom';
import Icon from '../icon/Icon.vue';
import Button from '../button/Button.vue';
import Paged from '../paged/Paged.vue';

export default {
    components: {
        'tnxel-icon': Icon,
        'tnxel-button': Button,
        'tnxel-paged': Paged
    },
    name: 'TnxelQueryTable',
    props: {
        url: {
            type: String,
            required: true,
        },
        modelValue: Object, // 查询参数
        size: String,
        border: {
            type: Boolean,
            default: true,
        },
        stripe: {
            type: Boolean,
            default: true,
        },
        showPaged: {
            type: Boolean,
            default: true,
        },
        pagedAlign: String,
        pagedSizes: Array,
        pagedLayout: String,
        success: Function,
        rowClassName: String,
        formatter: Function,
        order: Function,
        pagedChange: Function,
        selectable: { // 是否可选择
            type: [Boolean, String], // false-不可选择；true/'single'-可单选；'multi'-可多选但不可全选；'all'-可多选且可全选
            default() {
                return false;
            }
        },
        selectName: { // 比较已选对象与表格数据中行对象是否相等的字段名称，这要求数据对象必须具有唯一标识字段
            type: String,
            default() {
                return 'id';
            }
        },
        selected: [Object, Array], // 已选择的行对象
        init: Boolean,
        paramRequired: Boolean, // 是否至少需要一个查询参数
        appendMore: Boolean, // 是否记录追加模式，即当查询页码>1时，后续页记录是否追加到现有记录清单中，false-替代现有记录清单
        fixedHeight: Boolean, // 是否固定高度，true-表格的最大高度固定为容器的高度
        defaultExpandAll: Boolean,
        spanMethod: Function,
    },
    emits: ['update:modelValue', 'update:selected'],
    data() {
        return {
            id: window.tnx.util.string.random(16),
            params: this.getParams(this.modelValue),
            records: null,
            paged: null,
            querying: false,
            pageSelectedIndexes: [], // 当前页已选择记录的索引
            allSelectedRecords: this.selected || [], // 所有已选择的记录
            containerHeight: 0,
            tableKey: new Date().toString(),
        }
    },
    computed: {
        emptyParams() {
            if (this.params) {
                let keys = Object.keys(this.params);
                if (keys.length) {
                    let invalidKeys = ['pageSize', 'pageNo', 'orders', 'ignoring'];
                    for (let key of keys) {
                        if (!invalidKeys.contains(key)) {
                            let value = this.params[key];
                            if (value !== undefined && value !== null && value !== '') {
                                return false;
                            }
                        }
                    }
                }
            }
            return true;
        },
        tableMaxHeight() {
            if (this.fixedHeight && this.containerHeight) {
                return (this.paged && typeof this.paged.total === 'number') ? (this.containerHeight - 40) : this.containerHeight;
            }
            return undefined;
        },
        defaultSort() {
            let sortableColumnNames = [];
            if (this.$slots.default) {
                let columns = this.$slots.default();
                for (let column of columns) {
                    if (column.props.prop && column.props.sortable === 'custom') {
                        sortableColumnNames.push(column.props.prop);
                    }
                }
            }
            if (sortableColumnNames.length) {
                if (this.paged && this.paged.orders && this.paged.orders.length) {
                    let fieldOrder = this.paged.orders[0];
                    let fieldName = fieldOrder.name;
                    if (sortableColumnNames.contains(fieldName)) {
                        return {
                            prop: fieldName,
                            order: fieldOrder.desc ? 'descending' : 'ascending',
                        };
                    }
                }
                if (this.params && this.params.orderBy) {
                    let array = this.params.orderBy.split(' ');
                    let fieldName = array[0];
                    if (sortableColumnNames.contains(fieldName)) {
                        return {
                            prop: fieldName,
                            order: (array[1] || 'asc').toLowerCase() === 'desc' ? 'descending' : 'ascending',
                        }
                    }
                }
            }
            return undefined;
        },
        defaultSortString() {
            if (this.defaultSort) {
                return this.defaultSort.prop + ' ' + this.defaultSort.order;
            }
            return undefined;
        },
        pageAllSelected() {
            if (this.records && this.records.length) {
                let selected = null;
                for (let i = 0; i < this.records.length; i++) {
                    if (selected === null) {
                        selected = this.pageSelectedIndexes[i];
                    } else if (selected !== this.pageSelectedIndexes[i]) {
                        return null;
                    }
                }
                return selected;
            }
            return false;
        },
        allSelectedIndeterminate() {
            if (this.records && this.records.length) {
                let firstSelected = this.pageSelectedIndexes[0] || false;
                if (this.records.length === 1) {
                    return firstSelected;
                }
                for (let i = 1; i < this.records.length; i++) {
                    let selected = this.pageSelectedIndexes[i] || false;
                    if (selected !== firstSelected) {
                        return true;
                    }
                }
            }
            return false;
        },
    },
    watch: {
        modelValue: {
            handler: function (value) {
                this.params = this.getParams(value);
            },
            deep: true,
        },
        allSelectedRecords() {
            this.$emit('update:selected', this.allSelectedRecords);
        },
    },
    mounted() {
        if (this.init) {
            this.query();
        }
        this.$nextTick(() => {
            this.containerHeight = this.getContainerHeight();
            this.tableKey = this.id + '-table';
        });
    },
    methods: {
        getParams(modelValue) {
            return Object.assign({}, this.params, modelValue); // 避免改动传入的参数对象
        },
        onPagedChange(pageNo) {
            if (this.pagedChange && this.pagedChange(pageNo) === false) {
                return;
            }
            this.query(pageNo);
        },
        query(params) {
            if (typeof params === 'number') { // 参数为页码
                params = {pageNo: params};
            }

            if (typeof params === 'object') {
                // 指定了modelValue属性，在页码变更时需要触发更新事件
                let pageNoChanged = this.params.pageNo !== params.pageNo;
                this.params = this.getParams(params);
                this.params.pageNo = this.params.pageNo || 1;
                if (pageNoChanged && this.modelValue) {
                    this.$emit('update:modelValue', this.params);
                }
                // 带查询条件参数对象的为全新查询，清空已选清单
                this.pageSelectedIndexes = [];
                this.allSelectedRecords = [];
            }

            if (this.paramRequired && this.emptyParams) {
                this.records = null;
                this.paged = null;
            } else {
                this.querying = true;
                // 在追加记录模式中，如果查询页码大于1，则不清除现有记录，否则清除现有记录
                if (!this.appendMore || !this.params.pageNo || this.params.pageNo <= 1) {
                    this.records = null;
                    this.paged = null;
                }
                window.tnx.app.rpc.get(this.url, this.params).then(result => {
                    this.querying = false;
                    if (Array.isArray(result)) {
                        this.records = this.format(result);
                    } else {
                        let records = this.format(result.records);
                        if (result.paged.pageNo > 1 && this.appendMore) { // 追加记录
                            this.records = this.records || [];
                            // 结果页码大于当前页码才追加记录
                            if (result.paged.pageNo > this.paged.pageNo) {
                                this.records = this.records.concat(records);
                            }
                        } else { // 替代记录
                            this.records = records;
                        }
                        this.paged = result.paged;
                    }
                    this.selectAllToPage();
                    this.tableKey = new Date().toString();
                    if (this.success) {
                        this.success(this.records, this.paged);
                    }
                })
            }
        },
        format(records) {
            if (this.formatter) {
                return this.formatter(records);
            }
            return records;
        },
        sort(options) {
            if (options && options.prop && options.order) {
                this.params.orderBy = options.prop + (options.order === 'descending' ? ' desc' : '');
            } else {
                delete this.params.orderBy;
            }
            this.params.pageNo = 1;
            if (this.modelValue) {
                this.$emit('update:modelValue', this.params);
            }
            if (this.order && this.order(this.params.orderBy) === false) {
                return;
            }
            this.query();
        },
        selectRow(row, column, cell) {
            if (this.selectable && this.records && column.getColumnIndex() > 0 && !cell.innerHTML.contains('</a>')) {
                let index = window.tnx.util.array.indexOf(this.records, (element) => {
                    return row[this.selectName] === element[this.selectName];
                });
                if (index >= 0) {
                    this.pageSelectedIndexes[index] = !this.pageSelectedIndexes[index];
                    this.selectPageToAll();
                }
            }
        },
        selectAll(selected) {
            if (this.records && this.records.length) {
                for (let i = 0; i < this.records.length; i++) {
                    this.pageSelectedIndexes[i] = selected;
                }
                this.selectPageToAll();
            }
        },
        selectPageToAll() {
            for (let index = 0; index < this.pageSelectedIndexes.length; index++) {
                let selectedInPage = this.pageSelectedIndexes[index];
                let record = this.records[index];
                let fnEquals = (element) => {
                    return record[this.selectName] === element[this.selectName];
                };
                let selectedInAll = this.allSelectedRecords.contains(fnEquals);
                if (selectedInPage && !selectedInAll) { // 当前页已选但全局未选，则加入全局已选清单
                    this.allSelectedRecords.push(record);
                } else if (!selectedInPage && selectedInAll) { // 当前页未选但全局已选，则从全局已选中移除
                    this.allSelectedRecords.remove(fnEquals);
                }
            }
            this.$emit('update:selected', this.allSelectedRecords);
        },
        selectAllToPage() {
            if (this.selectable) {
                if (!Array.isArray(this.allSelectedRecords)) {
                    this.allSelectedRecords = [this.allSelectedRecords];
                }
                this.pageSelectedIndexes = [];
                for (let selectedRecord of this.allSelectedRecords) {
                    for (let i = 0; i < this.records.length; i++) {
                        let record = this.records[i];
                        if (record[this.selectName] === selectedRecord[this.selectName]) {
                            this.pageSelectedIndexes[i] = true;
                        }
                    }
                }
            }
        },
        scrollToTop() {
            if (this.tableMaxHeight) {
                this.$refs.table.setScrollTop(0);
            } else {
                window.tnx.util.dom.scrollToTop();
            }
        },
        toggleRowExpansion(row, expanded) {
            this.$refs.table.toggleRowExpansion(row, expanded);
        },
    }
}
</script>

<style>
.tnxel-query-table .el-table__empty-text {
    min-width: 260px;
    display: flex;
    align-items: center;
    justify-content: center;
}

.tnxel-query-table .el-table__append-wrapper {
    height: 41px;
    display: flex;
    align-items: center;
    justify-content: space-between;
}

.tnxel-query-table.selectable .el-table__row {
    cursor: pointer;
}

.tnxel-query-table.selectable .el-checkbox {
    height: auto;
}

.tnxel-query-table.selectable .el-table__cell:first-child .cell {
    display: flex;
    align-items: center;
    justify-content: center;
}
</style>
